#include <iostream>
#include "easylogging++.h"
#include "RtiHelper.h"
#include "Utils.h"

using namespace std;

RTIfedTime g_currentTime = 0.0;
bool g_bConstrained = false;
bool g_bRegulation = false;
bool g_granted = false;
bool socketOpen = true;
RTI::InteractionClassHandle hSumoClass;
RTI::InteractionClassHandle hSyncClass;

std::function<void(int)> clearNs3Rti;

void stopNs3RtiSignalHandle(int sigintNum){
    clearNs3Rti(sigintNum);
}

int ns3_rti(Ns3Arguments ns3Arguments) {
    RtiHelper rtiHelper(ns3Arguments);
    rtiHelper.initGlobalFlag(&g_currentTime, &g_bConstrained, &g_bRegulation, &g_granted);

    try {
        RTIambassadorReturn ambassadorReturn = rtiHelper.createFederationExecution();
        if (ambassadorReturn.code != 0)
            return ambassadorReturn.code;
        FederateHandleReturn federateHandleReturn = rtiHelper.joinFederationExecution();
        if (federateHandleReturn.code != 0)
            return federateHandleReturn.code;

        hSumoClass = rtiHelper.getInteractionClassHandle("sumo_car");
        hSyncClass = rtiHelper.getInteractionClassHandle("info_head");

        rtiHelper.subscribeInteractionClass(hSumoClass);
        rtiHelper.subscribeInteractionClass(hSyncClass);

        rtiHelper.enableTimeConstrained();
        rtiHelper.enableTimeRegulation(ns3Arguments.stepLength, 0.0001);
        rtiHelper.enableAsynchronousDelivery();

        signal(SIGINT, stopNs3RtiSignalHandle);
        clearNs3Rti = [&rtiHelper](int signum) {
            rtiHelper.resignFederationExecution();
            rtiHelper.destroyFederationExecution();
            LOG(INFO) << "Interrupt signal (" << signum << ") received.";
            exit(signum);
        };

        while (true) {
            rtiHelper.timeAdvanceRequest();
        }
    }
    catch (std::runtime_error &e) {
        LOG(ERROR) << "Error: " << e.what() << endl;
        return 510;
    }
    catch (RTI::Exception &e) {
        LOG(ERROR) << "FED_HW: ERROR:" << e << endl;
        return 511;
    }
    return 0;
}

int main(int argc, const char *argv[]) {
    Utils::logConfig(argc, argv);
    const Ns3Arguments ns3Arguments = Utils::parameterAnalysis(argc, argv);
    ns3_rti(ns3Arguments);
    return 0;
}
